#include "BoxRenderer.h"
#include "Base/NonWindow.h"
#include "Minecraft/Containers/Chunk.h"
#include "BlockVertexBufferSorter.h"
#include <assert.h>
#include <iostream>

#include <Windows.h>

BoxRenderer::BoxRenderer() : m_pRenderTarget(NULL)
{

}

BoxRenderer::~BoxRenderer()
{
	Shutdown();
}


BoxRenderer &BoxRenderer::GetInstance()
{
	static BoxRenderer instance;
	return instance;
}

void BoxRenderer::Init()
{
	// Set up our presentation paramaters
	g_D3D.pD3D = Direct3DCreate9(D3D_SDK_VERSION);
	ZeroMemory(&g_D3D.D3Dpp, sizeof(D3DPRESENT_PARAMETERS));

	g_D3D.D3Dpp.hDeviceWindow			= g_hWnd;
	g_D3D.D3Dpp.Windowed				= true;
	g_D3D.D3Dpp.PresentationInterval	= D3DPRESENT_INTERVAL_IMMEDIATE;
	g_D3D.D3Dpp.SwapEffect				= D3DSWAPEFFECT_DISCARD;

	g_D3D.D3Dpp.BackBufferCount			= 1;
	g_D3D.D3Dpp.BackBufferFormat		= D3DFMT_A8R8G8B8;
	g_D3D.D3Dpp.BackBufferWidth			= WINDOW_WIDTH;
	g_D3D.D3Dpp.BackBufferHeight		= WINDOW_HEIGHT;
	g_D3D.D3Dpp.Flags					= D3DPRESENTFLAG_LOCKABLE_BACKBUFFER | D3DPRESENTFLAG_DISCARD_DEPTHSTENCIL;

	g_D3D.D3Dpp.EnableAutoDepthStencil	= true;
	g_D3D.D3Dpp.AutoDepthStencilFormat	= D3DFMT_D24S8;

	// Create our device
	HRESULT hr = g_D3D.pD3D->CreateDevice(0, D3DDEVTYPE_HAL, g_hWnd, D3DCREATE_HARDWARE_VERTEXPROCESSING | D3DCREATE_MULTITHREADED, &g_D3D.D3Dpp, &g_D3D.pDev);
	assert(hr == S_OK && "Failed to create the DirectX device.");

	// Setup our sorter
	sorter.Init();

	// Create the shader (effect) and pull some handles into it out
	m_hWorld = BlockVertexBufferSorter::m_pEffect->GetParameterBySemantic(NULL, "WORLD");
	m_hView = BlockVertexBufferSorter::m_pEffect->GetParameterBySemantic(NULL, "VIEW");
	m_hProjection = BlockVertexBufferSorter::m_pEffect->GetParameterBySemantic(NULL, "PROJECTION");
	m_hTechnique = BlockVertexBufferSorter::m_pEffect->GetTechniqueByName("TShader_Instancing");
	m_hAlphaTechnique = BlockVertexBufferSorter::m_pEffect->GetTechniqueByName("TShader_Instancing_Alpha");
	m_hPosition = BlockVertexBufferSorter::m_pEffect->GetParameterBySemantic(NULL, "BOXINSTANCEARRAY_POSITION");
	
	// Height of our scene is sqrt(16896) ~ 130
	// Width of our scene is sqrt(512) ~ 22

	// Build our 'camera' (position is wrong)
	D3DXMatrixOrthoLH(&proj, sqrt(512.f) / 2.f, sqrt(16896.f) / 2.f, 1.f, 10000.f);
	D3DXMatrixLookAtLH(&view, &D3DXVECTOR3(-40.f, 61.5f, -40.f), &D3DXVECTOR3(8.f, 29.5f, 8.f), &D3DXVECTOR3(0.f, 1.f, 0.f));
	
	// Make our render target
	g_D3D.pDev->CreateTexture(WINDOW_WIDTH, WINDOW_HEIGHT, 1, D3DUSAGE_RENDERTARGET, D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT, &m_pRenderTarget, NULL);
	m_pRenderTarget->GetSurfaceLevel(0, &m_pRenderSurface);
}

void BoxRenderer::Shutdown()
{

}


void BoxRenderer::Render(const char *_szFileName)
{
	static BLOCK_TYPE solid[67] = { STONE, GRASS, DIRT, COBBLESTONE, WOOD, SAPLING, BEDROCK, SAND, GRAVEL, GOLD_ORE, IRON_ORE, COAL_ORE, LOG, 
						SPONGE, YELLOW_FLOWER, RED_ROSE, BROWN_MUSHROOM, RED_MUSHROOM, GOLD_BLOCK, IRON_BLOCK, DOUBLE_STEP,
						STEP, BRICK, TNT, BOOKSHELF, MOSSY_COBBLESTONE, OBSIDIAN, TORCH, FIRE, MOB_SPAWNER, WOODEN_STAIRS,
						CHEST, REDSTONE_WIRE, DIAMOND_ORE, DIAMOND_BLOCK, WORKBENCH, CROPS, SOIL, FURNACE, BURNING_FURNACE,
						SIGN_POST, WOODEN_DOOR, LADDER, MINECART_TRACKS, COBBLESTONE_STAIRS, WALL_SIGN, LEVER, STONE_PRESSURE_PLATE,
						IRON_DOOR, WOODEN_PRESSURE_PLATE, REDSTONE_ORE, GLOWING_REDSTONE_ORE, REDSTONE_TORCH_OFF, REDSTONE_TORCH_ON,
						STONE_BUTTON, SNOW, SNOW_BLOCK, CACTUS, CLAY, REED, JUKEBOX, FENCE, PUMPKIN, NETHERSTONE, SLOW_SAND,
						LIGHTSTONE, JACK_O_LANTERN };
	static BLOCK_TYPE alpha[8] = { WATER, STATIONARY_WATER, LAVA, STATIONARY_LAVA, LEAVES, GLASS, ICE, PORTAL };

	float start, stop, freq;
	LARGE_INTEGER ticks;
	LARGE_INTEGER tempFreq;
	QueryPerformanceFrequency(&tempFreq);
	freq = (float)tempFreq.QuadPart;
	QueryPerformanceCounter(&ticks);
	start = (float)ticks.QuadPart / freq;

	// Make sure we're looking at our render target
	g_D3D.pDev->SetRenderTarget(0, m_pRenderSurface);

	g_D3D.pDev->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, 0, 1.f, 0);
	g_D3D.pDev->BeginScene();

	// Update our camera
	D3DXMATRIX I;
	BlockVertexBufferSorter::m_pEffect->SetMatrix(m_hWorld, D3DXMatrixIdentity(&I));
	BlockVertexBufferSorter::m_pEffect->SetMatrix(m_hView, &view);
	BlockVertexBufferSorter::m_pEffect->SetMatrix(m_hProjection, &proj);
	BlockVertexBufferSorter::m_pEffect->SetTechnique(m_hTechnique);
	
	// Render solid
	for (int iType = 0; iType < 67; ++iType)
		sorter.Render(solid[iType]);

	BlockVertexBufferSorter::m_pEffect->SetTechnique(m_hAlphaTechnique);

	// Render alpha
	for (int iType = 0; iType < 8; ++iType)
		sorter.Render(alpha[iType]);

	g_D3D.pDev->EndScene();
	g_D3D.pDev->Present(0, 0, 0, 0);

	D3DXSaveSurfaceToFile(_szFileName, D3DXIFF_PNG, m_pRenderSurface, NULL, NULL);
	
	QueryPerformanceCounter(&ticks);
	stop = (float)ticks.QuadPart / freq;
	std::cout << stop - start;
}